{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用Embedding回答问题\n",
    "\n",
    "许多用例需要 GPT-3 以富有洞察力的答案来回答用户问题。例如，客户支持聊天机器人可能需要提供常见问题的答案。GPT 模型在训练中学到了很多常识，但我们经常需要摄取和使用一个包含更具体信息的大型库。\n",
    "\n",
    "在本示例中，将演示一种使 GPT-3 能够使用文本库作为参考、使用文档嵌入和检索来回答问题的方法。我们将使用有关2020年夏季奥运会的维基百科文章数据集。请参阅[此笔记本]（fine-tuned_qa/olympics-1-collect-data.ipynb）以跟踪数据收集过程。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import openai\n",
    "import pandas as pd\n",
    "import pickle\n",
    "import tiktoken\n",
    "import os\n",
    "COMPLETIONS_MODEL = \"text-davinci-003\"\n",
    "EMBEDDING_MODEL = \"text-embedding-ada-002\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'2020年夏季奥运会男子跳高冠军是中国选手高洪波。'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 默认情况下，GPT-3 不是 2020 年奥运会的专家：\n",
    "prompt = \"谁赢得了2020年夏季奥运会男子跳高?\"\n",
    "\n",
    "openai.Completion.create(\n",
    "    prompt=prompt,\n",
    "    temperature=0,\n",
    "    max_tokens=300,\n",
    "    model=COMPLETIONS_MODEL\n",
    ")[\"choices\"][0][\"text\"].strip(\" \\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "高洪波是中国足协副主席，他不是一个跳高运动员！显然，GPT-3 在这里需要一些帮助。\n",
    "\n",
    "首先，模型正在产生幻觉，而不是告诉我们“我不知道”。这很糟糕，因为这就让人很难相信模型给出的答案！\n",
    "\n",
    "# 0） 通过提示引擎预防幻觉\n",
    "\n",
    "我们可以通过更明确地使用提示来解决这个幻觉问题："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'对不起我不知道.'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt = \"\"\"Answer the question as truthfully as possible, and if you're unsure of the answer, say \"对不起我不知道.\".\n",
    "\n",
    "Q: 谁赢得了2020年夏季奥运会男子跳高?\n",
    "A:\"\"\"\n",
    "openai.Completion.create(\n",
    "    prompt=prompt,\n",
    "    temperature=0,\n",
    "    max_tokens=300,\n",
    "    model=COMPLETIONS_MODEL\n",
    ")[\"choices\"][0][\"text\"].strip(\" \\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'意大利运动员Gianmarco Tamberi和卡塔尔运动员Mutaz Essa Barshim.'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 为了帮助模型回答问题，可在提示中提供了额外的上下文信息。\n",
    "# 当所需的总上下文较短时，可以直接将其包含在提示中。\n",
    "# 例如，可以使用从维基百科中获取的这些信息。\n",
    "# 更新初始提示，以告知模型显式使用提供的文本。\n",
    "\n",
    "prompt = \"\"\"Answer the question as truthfully as possible using the provided text, and if the answer is not contained within the text below, say \"I don't know\"\n",
    "\n",
    "Context:\n",
    "2020年夏季奥运会男子跳高比赛于2021年7月30日至8月1日在奥林匹克体育场举行，来自24个国家的33名运动员参加了比赛;可能的总数取决于除了通过分数或排名获得的 32 个资格赛之外，还有多少国家将使用普遍性名额进入运动员（2021 年没有使用通用性名额）。意大利运动员Gianmarco Tamberi和卡塔尔运动员Mutaz Essa Barshim在他们两人之间以2.37米的成绩平局后成为该赛事的共同获胜者。坦贝里和巴希姆都同意分享金牌，这是不同国家的运动员在奥运会历史上同意分享同一枚奖牌的罕见情况。特别是听到巴希姆问一位比赛官员“我们可以有两枚金牌吗？”以回应被提供“跳伞”。白俄罗斯的马克西姆·内达斯考获得铜牌。这些奖牌是意大利和白俄罗斯男子跳高的第一枚金牌，意大利和卡塔尔男子跳高的第一枚金牌，卡塔尔男子跳高的第三枚连续奖牌（全部由巴希姆获得）。巴希姆成为继瑞典的帕特里克·舍伯格（1984年至1992年）之后第二位获得三枚跳高奖牌的人\n",
    "\n",
    "Q: 谁赢得了2020年夏季奥运会男子跳高?\n",
    "A:\"\"\"\n",
    "\n",
    "openai.Completion.create(\n",
    "    prompt=prompt,\n",
    "    temperature=0,\n",
    "    max_tokens=300,\n",
    "    top_p=1,\n",
    "    frequency_penalty=0,\n",
    "    presence_penalty=0,\n",
    "    model=COMPLETIONS_MODEL\n",
    ")[\"choices\"][0][\"text\"].strip(\" \\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "仅当模型可能需要知道的额外内容数据集足够小以适合单个提示时，才向提示中添加额外信息。当我们需要模型从大量信息中选择相关的上下文信息时，我们该怎么办？\n",
    "\n",
    "**在本示例的其余部分中，我们将演示一种通过使用文档嵌入和检索来使用大量其他上下文信息来增强 GPT-3 的方法。 此方法分两步回答查询：首先，它检索与查询相关的信息，然后根据检索到的信息编写针对问题量身定制的答案。第一步使用 [Embeddings API]（https://beta.openai.com/docs/guides/embeddings），第二步使用 [Completions API]（https://beta.openai.com/docs/guides/completion/introduction）。\n",
    " \n",
    "步骤如下：\n",
    "* 通过将上下文信息拆分为块来预处理上下文信息，并为每个块创建一个嵌入向量。\n",
    "* 收到查询后，将查询嵌入与上下文块相同的向量空间中，并找到与查询最相似的上下文嵌入。\n",
    "* 将最相关的上下文嵌入附加到查询提示符之前。\n",
    "* 将问题与最相关的上下文一起提交给 GPT，并收到利用所提供的上下文信息的答案。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1） 预处理文档库\n",
    "\n",
    "我们计划使用文档嵌入来获取文档库中最相关的部分，并将它们插入到我们提供给 GPT-3 的提示中。因此，我们需要将文档库分解为上下文的“部分”，可以单独搜索和检索。\n",
    "\n",
    "部分应足够大，以包含足够的信息来回答问题;但足够小，可以将一个或多个放入 GPT-3 提示符中。我们发现大约一段文本通常是一个不错的长度，但您应该针对您的特定用例进行试验。在此示例中，维基百科文章已经分组为语义相关的标题，因此我们将使用这些标题来定义我们的部分。这个预处理已经在[这示例]（fine-tuned_qa/olympics-1-collect-data.ipynb）中完成，所以我们将加载结果并使用它们。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3964 rows in the data.\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th>content</th>\n",
       "      <th>tokens</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>title</th>\n",
       "      <th>heading</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Iran at the 2020 Summer Olympics</th>\n",
       "      <th>Karate</th>\n",
       "      <td>Iran entered three karateka into the inaugural...</td>\n",
       "      <td>87</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Beach volleyball at the 2020 Summer Olympics – Men's tournament</th>\n",
       "      <th>Lucky losers</th>\n",
       "      <td>The table below shows the ranking of third-pla...</td>\n",
       "      <td>66</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Fencing at the 2020 Summer Olympics – Men's sabre</th>\n",
       "      <th>Background</th>\n",
       "      <td>This will be the 29th appearance of the event,...</td>\n",
       "      <td>83</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Basketball at the 2020 Summer Olympics – Women's 3x3 tournament</th>\n",
       "      <th>Format</th>\n",
       "      <td>The eight teams played a round robin. The team...</td>\n",
       "      <td>44</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2020 United States Olympic Team Trials (wrestling)</th>\n",
       "      <th>Direct qualification</th>\n",
       "      <td>To qualify for the 2020 U.S. Olympic Team Tria...</td>\n",
       "      <td>49</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                                                                                                   content  \\\n",
       "title                                              heading                                                                   \n",
       "Iran at the 2020 Summer Olympics                   Karate                Iran entered three karateka into the inaugural...   \n",
       "Beach volleyball at the 2020 Summer Olympics – ... Lucky losers          The table below shows the ranking of third-pla...   \n",
       "Fencing at the 2020 Summer Olympics – Men's sabre  Background            This will be the 29th appearance of the event,...   \n",
       "Basketball at the 2020 Summer Olympics – Women'... Format                The eight teams played a round robin. The team...   \n",
       "2020 United States Olympic Team Trials (wrestling) Direct qualification  To qualify for the 2020 U.S. Olympic Team Tria...   \n",
       "\n",
       "                                                                         tokens  \n",
       "title                                              heading                       \n",
       "Iran at the 2020 Summer Olympics                   Karate                    87  \n",
       "Beach volleyball at the 2020 Summer Olympics – ... Lucky losers              66  \n",
       "Fencing at the 2020 Summer Olympics – Men's sabre  Background                83  \n",
       "Basketball at the 2020 Summer Olympics – Women'... Format                    44  \n",
       "2020 United States Olympic Team Trials (wrestling) Direct qualification      49  "
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 我们已经托管了已处理的数据集，因此您可以直接下载它，而无需重新创建它。\n",
    "# 此数据集已被拆分为多个部分，维基百科页面的每个部分各占一行。\n",
    "\n",
    "df = pd.read_csv('https://cdn.openai.com/API/examples/data/olympics_sections_text.csv')\n",
    "df = df.set_index([\"title\", \"heading\"])\n",
    "print(f\"{len(df)} rows in the data.\")\n",
    "df.sample(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过为每个部分创建一个嵌入向量来预处理文档部分。嵌入是数字向量，可以帮助理解文本在语义上的相似或不同程度。两个嵌入越接近，它们的内容就越相似。有关更多信息，请参阅 [OpenAI 嵌入文档]（https://beta.openai.com/docs/guides/embeddings）。\n",
    "\n",
    "此索引阶段可以脱机执行，并且仅运行一次以预先计算数据集的索引，以便以后可以检索每条内容。由于这是一个小示例，我们将在本地存储和搜索嵌入。如果你有一个更大的数据集，考虑使用矢量搜索引擎，如[Pinecone]（https://www.pinecone.io/）或[Weaviate]（https://github.com/semi-technologies/weaviate）来支持搜索。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_embedding(text: str, model: str=EMBEDDING_MODEL) -> list[float]:\n",
    "    result = openai.Embedding.create(\n",
    "      model=model,\n",
    "      input=text\n",
    "    )\n",
    "    return result[\"data\"][0][\"embedding\"]\n",
    "\n",
    "def compute_doc_embeddings(df: pd.DataFrame) -> dict[tuple[str, str], list[float]]:\n",
    "    \"\"\"\n",
    "    Create an embedding for each row in the dataframe using the OpenAI Embeddings API.\n",
    "    \n",
    "    Return a dictionary that maps between each embedding vector and the index of the row that it corresponds to.\n",
    "    \"\"\"\n",
    "    return {\n",
    "        idx: get_embedding(r.content) for idx, r in df.iterrows()\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_embeddings(fname: str) -> dict[tuple[str, str], list[float]]:\n",
    "    \"\"\"\n",
    "    Read the document embeddings and their keys from a CSV.\n",
    "    \n",
    "    fname is the path to a CSV with exactly these named columns: \n",
    "        \"title\", \"heading\", \"0\", \"1\", ... up to the length of the embedding vectors.\n",
    "    \"\"\"\n",
    "    \n",
    "    df = pd.read_csv(fname, header=0)\n",
    "    max_dim = max([int(c) for c in df.columns if c != \"title\" and c != \"heading\"])\n",
    "    return {\n",
    "           (r.title, r.heading): [r[str(i)] for i in range(max_dim + 1)] for _, r in df.iterrows()\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[0;32mIn [13], line 6\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[39m# 对此embeddings做了托管, 可直接下载无需计算\u001b[39;00m\n\u001b[1;32m      2\u001b[0m \u001b[39m# document_embeddings = load_embeddings(\"https://cdn.openai.com/API/examples/data/olympics_sections_document_embeddings.csv\")\u001b[39;00m\n\u001b[1;32m      3\u001b[0m \n\u001b[1;32m      4\u001b[0m \u001b[39m# ===== 或使用下面一行代码来重新计算embeddings ========\u001b[39;00m\n\u001b[0;32m----> 6\u001b[0m document_embeddings \u001b[39m=\u001b[39m compute_doc_embeddings(df)\n",
      "Cell \u001b[0;32mIn [10], line 14\u001b[0m, in \u001b[0;36mcompute_doc_embeddings\u001b[0;34m(df)\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mcompute_doc_embeddings\u001b[39m(df: pd\u001b[39m.\u001b[39mDataFrame) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m \u001b[39mdict\u001b[39m[\u001b[39mtuple\u001b[39m[\u001b[39mstr\u001b[39m, \u001b[39mstr\u001b[39m], \u001b[39mlist\u001b[39m[\u001b[39mfloat\u001b[39m]]:\n\u001b[1;32m      9\u001b[0m     \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m     10\u001b[0m \u001b[39m    Create an embedding for each row in the dataframe using the OpenAI Embeddings API.\u001b[39;00m\n\u001b[1;32m     11\u001b[0m \u001b[39m    \u001b[39;00m\n\u001b[1;32m     12\u001b[0m \u001b[39m    Return a dictionary that maps between each embedding vector and the index of the row that it corresponds to.\u001b[39;00m\n\u001b[1;32m     13\u001b[0m \u001b[39m    \"\"\"\u001b[39;00m\n\u001b[0;32m---> 14\u001b[0m     \u001b[39mreturn\u001b[39;00m {\n\u001b[1;32m     15\u001b[0m         idx: get_embedding(r\u001b[39m.\u001b[39mcontent) \u001b[39mfor\u001b[39;00m idx, r \u001b[39min\u001b[39;00m df\u001b[39m.\u001b[39miterrows()\n\u001b[1;32m     16\u001b[0m     }\n",
      "Cell \u001b[0;32mIn [10], line 15\u001b[0m, in \u001b[0;36m<dictcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mcompute_doc_embeddings\u001b[39m(df: pd\u001b[39m.\u001b[39mDataFrame) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m \u001b[39mdict\u001b[39m[\u001b[39mtuple\u001b[39m[\u001b[39mstr\u001b[39m, \u001b[39mstr\u001b[39m], \u001b[39mlist\u001b[39m[\u001b[39mfloat\u001b[39m]]:\n\u001b[1;32m      9\u001b[0m     \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m     10\u001b[0m \u001b[39m    Create an embedding for each row in the dataframe using the OpenAI Embeddings API.\u001b[39;00m\n\u001b[1;32m     11\u001b[0m \u001b[39m    \u001b[39;00m\n\u001b[1;32m     12\u001b[0m \u001b[39m    Return a dictionary that maps between each embedding vector and the index of the row that it corresponds to.\u001b[39;00m\n\u001b[1;32m     13\u001b[0m \u001b[39m    \"\"\"\u001b[39;00m\n\u001b[1;32m     14\u001b[0m     \u001b[39mreturn\u001b[39;00m {\n\u001b[0;32m---> 15\u001b[0m         idx: get_embedding(r\u001b[39m.\u001b[39;49mcontent) \u001b[39mfor\u001b[39;00m idx, r \u001b[39min\u001b[39;00m df\u001b[39m.\u001b[39miterrows()\n\u001b[1;32m     16\u001b[0m     }\n",
      "Cell \u001b[0;32mIn [10], line 2\u001b[0m, in \u001b[0;36mget_embedding\u001b[0;34m(text, model)\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mget_embedding\u001b[39m(text: \u001b[39mstr\u001b[39m, model: \u001b[39mstr\u001b[39m\u001b[39m=\u001b[39mEMBEDDING_MODEL) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m \u001b[39mlist\u001b[39m[\u001b[39mfloat\u001b[39m]:\n\u001b[0;32m----> 2\u001b[0m     result \u001b[39m=\u001b[39m openai\u001b[39m.\u001b[39;49mEmbedding\u001b[39m.\u001b[39;49mcreate(\n\u001b[1;32m      3\u001b[0m       model\u001b[39m=\u001b[39;49mmodel,\n\u001b[1;32m      4\u001b[0m       \u001b[39minput\u001b[39;49m\u001b[39m=\u001b[39;49mtext\n\u001b[1;32m      5\u001b[0m     )\n\u001b[1;32m      6\u001b[0m     \u001b[39mreturn\u001b[39;00m result[\u001b[39m\"\u001b[39m\u001b[39mdata\u001b[39m\u001b[39m\"\u001b[39m][\u001b[39m0\u001b[39m][\u001b[39m\"\u001b[39m\u001b[39membedding\u001b[39m\u001b[39m\"\u001b[39m]\n",
      "File \u001b[0;32m/media/tlw/move/py310/lib/python3.10/site-packages/openai/api_resources/embedding.py:33\u001b[0m, in \u001b[0;36mEmbedding.create\u001b[0;34m(cls, *args, **kwargs)\u001b[0m\n\u001b[1;32m     31\u001b[0m \u001b[39mwhile\u001b[39;00m \u001b[39mTrue\u001b[39;00m:\n\u001b[1;32m     32\u001b[0m     \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m---> 33\u001b[0m         response \u001b[39m=\u001b[39m \u001b[39msuper\u001b[39;49m()\u001b[39m.\u001b[39;49mcreate(\u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs)\n\u001b[1;32m     35\u001b[0m         \u001b[39m# If a user specifies base64, we'll just return the encoded string.\u001b[39;00m\n\u001b[1;32m     36\u001b[0m         \u001b[39m# This is only for the default case.\u001b[39;00m\n\u001b[1;32m     37\u001b[0m         \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m user_provided_encoding_format:\n",
      "File \u001b[0;32m/media/tlw/move/py310/lib/python3.10/site-packages/openai/api_resources/abstract/engine_api_resource.py:153\u001b[0m, in \u001b[0;36mEngineAPIResource.create\u001b[0;34m(cls, api_key, api_base, api_type, request_id, api_version, organization, **params)\u001b[0m\n\u001b[1;32m    127\u001b[0m \u001b[39m@classmethod\u001b[39m\n\u001b[1;32m    128\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mcreate\u001b[39m(\n\u001b[1;32m    129\u001b[0m     \u001b[39mcls\u001b[39m,\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    136\u001b[0m     \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mparams,\n\u001b[1;32m    137\u001b[0m ):\n\u001b[1;32m    138\u001b[0m     (\n\u001b[1;32m    139\u001b[0m         deployment_id,\n\u001b[1;32m    140\u001b[0m         engine,\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    150\u001b[0m         api_key, api_base, api_type, api_version, organization, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mparams\n\u001b[1;32m    151\u001b[0m     )\n\u001b[0;32m--> 153\u001b[0m     response, _, api_key \u001b[39m=\u001b[39m requestor\u001b[39m.\u001b[39;49mrequest(\n\u001b[1;32m    154\u001b[0m         \u001b[39m\"\u001b[39;49m\u001b[39mpost\u001b[39;49m\u001b[39m\"\u001b[39;49m,\n\u001b[1;32m    155\u001b[0m         url,\n\u001b[1;32m    156\u001b[0m         params\u001b[39m=\u001b[39;49mparams,\n\u001b[1;32m    157\u001b[0m         headers\u001b[39m=\u001b[39;49mheaders,\n\u001b[1;32m    158\u001b[0m         stream\u001b[39m=\u001b[39;49mstream,\n\u001b[1;32m    159\u001b[0m         request_id\u001b[39m=\u001b[39;49mrequest_id,\n\u001b[1;32m    160\u001b[0m         request_timeout\u001b[39m=\u001b[39;49mrequest_timeout,\n\u001b[1;32m    161\u001b[0m     )\n\u001b[1;32m    163\u001b[0m     \u001b[39mif\u001b[39;00m stream:\n\u001b[1;32m    164\u001b[0m         \u001b[39m# must be an iterator\u001b[39;00m\n\u001b[1;32m    165\u001b[0m         \u001b[39massert\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39misinstance\u001b[39m(response, OpenAIResponse)\n",
      "File \u001b[0;32m/media/tlw/move/py310/lib/python3.10/site-packages/openai/api_requestor.py:217\u001b[0m, in \u001b[0;36mAPIRequestor.request\u001b[0;34m(self, method, url, params, headers, files, stream, request_id, request_timeout)\u001b[0m\n\u001b[1;32m    206\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mrequest\u001b[39m(\n\u001b[1;32m    207\u001b[0m     \u001b[39mself\u001b[39m,\n\u001b[1;32m    208\u001b[0m     method,\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    215\u001b[0m     request_timeout: Optional[Union[\u001b[39mfloat\u001b[39m, Tuple[\u001b[39mfloat\u001b[39m, \u001b[39mfloat\u001b[39m]]] \u001b[39m=\u001b[39m \u001b[39mNone\u001b[39;00m,\n\u001b[1;32m    216\u001b[0m ) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], \u001b[39mbool\u001b[39m, \u001b[39mstr\u001b[39m]:\n\u001b[0;32m--> 217\u001b[0m     result \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mrequest_raw(\n\u001b[1;32m    218\u001b[0m         method\u001b[39m.\u001b[39;49mlower(),\n\u001b[1;32m    219\u001b[0m         url,\n\u001b[1;32m    220\u001b[0m         params\u001b[39m=\u001b[39;49mparams,\n\u001b[1;32m    221\u001b[0m         supplied_headers\u001b[39m=\u001b[39;49mheaders,\n\u001b[1;32m    222\u001b[0m         files\u001b[39m=\u001b[39;49mfiles,\n\u001b[1;32m    223\u001b[0m         stream\u001b[39m=\u001b[39;49mstream,\n\u001b[1;32m    224\u001b[0m         request_id\u001b[39m=\u001b[39;49mrequest_id,\n\u001b[1;32m    225\u001b[0m         request_timeout\u001b[39m=\u001b[39;49mrequest_timeout,\n\u001b[1;32m    226\u001b[0m     )\n\u001b[1;32m    227\u001b[0m     resp, got_stream \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_interpret_response(result, stream)\n\u001b[1;32m    228\u001b[0m     \u001b[39mreturn\u001b[39;00m resp, got_stream, \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mapi_key\n",
      "File \u001b[0;32m/media/tlw/move/py310/lib/python3.10/site-packages/openai/api_requestor.py:517\u001b[0m, in \u001b[0;36mAPIRequestor.request_raw\u001b[0;34m(self, method, url, params, supplied_headers, files, stream, request_id, request_timeout)\u001b[0m\n\u001b[1;32m    515\u001b[0m     _thread_context\u001b[39m.\u001b[39msession \u001b[39m=\u001b[39m _make_session()\n\u001b[1;32m    516\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m--> 517\u001b[0m     result \u001b[39m=\u001b[39m _thread_context\u001b[39m.\u001b[39;49msession\u001b[39m.\u001b[39;49mrequest(\n\u001b[1;32m    518\u001b[0m         method,\n\u001b[1;32m    519\u001b[0m         abs_url,\n\u001b[1;32m    520\u001b[0m         headers\u001b[39m=\u001b[39;49mheaders,\n\u001b[1;32m    521\u001b[0m         data\u001b[39m=\u001b[39;49mdata,\n\u001b[1;32m    522\u001b[0m         files\u001b[39m=\u001b[39;49mfiles,\n\u001b[1;32m    523\u001b[0m         stream\u001b[39m=\u001b[39;49mstream,\n\u001b[1;32m    524\u001b[0m         timeout\u001b[39m=\u001b[39;49mrequest_timeout \u001b[39mif\u001b[39;49;00m request_timeout \u001b[39melse\u001b[39;49;00m TIMEOUT_SECS,\n\u001b[1;32m    525\u001b[0m     )\n\u001b[1;32m    526\u001b[0m \u001b[39mexcept\u001b[39;00m requests\u001b[39m.\u001b[39mexceptions\u001b[39m.\u001b[39mTimeout \u001b[39mas\u001b[39;00m e:\n\u001b[1;32m    527\u001b[0m     \u001b[39mraise\u001b[39;00m error\u001b[39m.\u001b[39mTimeout(\u001b[39m\"\u001b[39m\u001b[39mRequest timed out: \u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m\"\u001b[39m\u001b[39m.\u001b[39mformat(e)) \u001b[39mfrom\u001b[39;00m \u001b[39me\u001b[39;00m\n",
      "File \u001b[0;32m/media/tlw/move/py310/lib/python3.10/site-packages/requests/sessions.py:587\u001b[0m, in \u001b[0;36mSession.request\u001b[0;34m(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)\u001b[0m\n\u001b[1;32m    582\u001b[0m send_kwargs \u001b[39m=\u001b[39m {\n\u001b[1;32m    583\u001b[0m     \u001b[39m\"\u001b[39m\u001b[39mtimeout\u001b[39m\u001b[39m\"\u001b[39m: timeout,\n\u001b[1;32m    584\u001b[0m     \u001b[39m\"\u001b[39m\u001b[39mallow_redirects\u001b[39m\u001b[39m\"\u001b[39m: allow_redirects,\n\u001b[1;32m    585\u001b[0m }\n\u001b[1;32m    586\u001b[0m send_kwargs\u001b[39m.\u001b[39mupdate(settings)\n\u001b[0;32m--> 587\u001b[0m resp \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49msend(prep, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49msend_kwargs)\n\u001b[1;32m    589\u001b[0m \u001b[39mreturn\u001b[39;00m resp\n",
      "File \u001b[0;32m/media/tlw/move/py310/lib/python3.10/site-packages/requests/sessions.py:701\u001b[0m, in \u001b[0;36mSession.send\u001b[0;34m(self, request, **kwargs)\u001b[0m\n\u001b[1;32m    698\u001b[0m start \u001b[39m=\u001b[39m preferred_clock()\n\u001b[1;32m    700\u001b[0m \u001b[39m# Send the request\u001b[39;00m\n\u001b[0;32m--> 701\u001b[0m r \u001b[39m=\u001b[39m adapter\u001b[39m.\u001b[39;49msend(request, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs)\n\u001b[1;32m    703\u001b[0m \u001b[39m# Total elapsed time of the request (approximately)\u001b[39;00m\n\u001b[1;32m    704\u001b[0m elapsed \u001b[39m=\u001b[39m preferred_clock() \u001b[39m-\u001b[39m start\n",
      "File \u001b[0;32m/media/tlw/move/py310/lib/python3.10/site-packages/requests/adapters.py:489\u001b[0m, in \u001b[0;36mHTTPAdapter.send\u001b[0;34m(self, request, stream, timeout, verify, cert, proxies)\u001b[0m\n\u001b[1;32m    487\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m    488\u001b[0m     \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m chunked:\n\u001b[0;32m--> 489\u001b[0m         resp \u001b[39m=\u001b[39m conn\u001b[39m.\u001b[39;49murlopen(\n\u001b[1;32m    490\u001b[0m             method\u001b[39m=\u001b[39;49mrequest\u001b[39m.\u001b[39;49mmethod,\n\u001b[1;32m    491\u001b[0m             url\u001b[39m=\u001b[39;49murl,\n\u001b[1;32m    492\u001b[0m             body\u001b[39m=\u001b[39;49mrequest\u001b[39m.\u001b[39;49mbody,\n\u001b[1;32m    493\u001b[0m             headers\u001b[39m=\u001b[39;49mrequest\u001b[39m.\u001b[39;49mheaders,\n\u001b[1;32m    494\u001b[0m             redirect\u001b[39m=\u001b[39;49m\u001b[39mFalse\u001b[39;49;00m,\n\u001b[1;32m    495\u001b[0m             assert_same_host\u001b[39m=\u001b[39;49m\u001b[39mFalse\u001b[39;49;00m,\n\u001b[1;32m    496\u001b[0m             preload_content\u001b[39m=\u001b[39;49m\u001b[39mFalse\u001b[39;49;00m,\n\u001b[1;32m    497\u001b[0m             decode_content\u001b[39m=\u001b[39;49m\u001b[39mFalse\u001b[39;49;00m,\n\u001b[1;32m    498\u001b[0m             retries\u001b[39m=\u001b[39;49m\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mmax_retries,\n\u001b[1;32m    499\u001b[0m             timeout\u001b[39m=\u001b[39;49mtimeout,\n\u001b[1;32m    500\u001b[0m         )\n\u001b[1;32m    502\u001b[0m     \u001b[39m# Send the request.\u001b[39;00m\n\u001b[1;32m    503\u001b[0m     \u001b[39melse\u001b[39;00m:\n\u001b[1;32m    504\u001b[0m         \u001b[39mif\u001b[39;00m \u001b[39mhasattr\u001b[39m(conn, \u001b[39m\"\u001b[39m\u001b[39mproxy_pool\u001b[39m\u001b[39m\"\u001b[39m):\n",
      "File \u001b[0;32m/media/tlw/move/py310/lib/python3.10/site-packages/urllib3/connectionpool.py:703\u001b[0m, in \u001b[0;36mHTTPConnectionPool.urlopen\u001b[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)\u001b[0m\n\u001b[1;32m    700\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_prepare_proxy(conn)\n\u001b[1;32m    702\u001b[0m \u001b[39m# Make the request on the httplib connection object.\u001b[39;00m\n\u001b[0;32m--> 703\u001b[0m httplib_response \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_make_request(\n\u001b[1;32m    704\u001b[0m     conn,\n\u001b[1;32m    705\u001b[0m     method,\n\u001b[1;32m    706\u001b[0m     url,\n\u001b[1;32m    707\u001b[0m     timeout\u001b[39m=\u001b[39;49mtimeout_obj,\n\u001b[1;32m    708\u001b[0m     body\u001b[39m=\u001b[39;49mbody,\n\u001b[1;32m    709\u001b[0m     headers\u001b[39m=\u001b[39;49mheaders,\n\u001b[1;32m    710\u001b[0m     chunked\u001b[39m=\u001b[39;49mchunked,\n\u001b[1;32m    711\u001b[0m )\n\u001b[1;32m    713\u001b[0m \u001b[39m# If we're going to release the connection in ``finally:``, then\u001b[39;00m\n\u001b[1;32m    714\u001b[0m \u001b[39m# the response doesn't need to know about the connection. Otherwise\u001b[39;00m\n\u001b[1;32m    715\u001b[0m \u001b[39m# it will also try to release it and we'll have a double-release\u001b[39;00m\n\u001b[1;32m    716\u001b[0m \u001b[39m# mess.\u001b[39;00m\n\u001b[1;32m    717\u001b[0m response_conn \u001b[39m=\u001b[39m conn \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m release_conn \u001b[39melse\u001b[39;00m \u001b[39mNone\u001b[39;00m\n",
      "File \u001b[0;32m/media/tlw/move/py310/lib/python3.10/site-packages/urllib3/connectionpool.py:449\u001b[0m, in \u001b[0;36mHTTPConnectionPool._make_request\u001b[0;34m(self, conn, method, url, timeout, chunked, **httplib_request_kw)\u001b[0m\n\u001b[1;32m    444\u001b[0m             httplib_response \u001b[39m=\u001b[39m conn\u001b[39m.\u001b[39mgetresponse()\n\u001b[1;32m    445\u001b[0m         \u001b[39mexcept\u001b[39;00m \u001b[39mBaseException\u001b[39;00m \u001b[39mas\u001b[39;00m e:\n\u001b[1;32m    446\u001b[0m             \u001b[39m# Remove the TypeError from the exception chain in\u001b[39;00m\n\u001b[1;32m    447\u001b[0m             \u001b[39m# Python 3 (including for exceptions like SystemExit).\u001b[39;00m\n\u001b[1;32m    448\u001b[0m             \u001b[39m# Otherwise it looks like a bug in the code.\u001b[39;00m\n\u001b[0;32m--> 449\u001b[0m             six\u001b[39m.\u001b[39;49mraise_from(e, \u001b[39mNone\u001b[39;49;00m)\n\u001b[1;32m    450\u001b[0m \u001b[39mexcept\u001b[39;00m (SocketTimeout, BaseSSLError, SocketError) \u001b[39mas\u001b[39;00m e:\n\u001b[1;32m    451\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_raise_timeout(err\u001b[39m=\u001b[39me, url\u001b[39m=\u001b[39murl, timeout_value\u001b[39m=\u001b[39mread_timeout)\n",
      "File \u001b[0;32m<string>:3\u001b[0m, in \u001b[0;36mraise_from\u001b[0;34m(value, from_value)\u001b[0m\n",
      "File \u001b[0;32m/media/tlw/move/py310/lib/python3.10/site-packages/urllib3/connectionpool.py:444\u001b[0m, in \u001b[0;36mHTTPConnectionPool._make_request\u001b[0;34m(self, conn, method, url, timeout, chunked, **httplib_request_kw)\u001b[0m\n\u001b[1;32m    441\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mTypeError\u001b[39;00m:\n\u001b[1;32m    442\u001b[0m     \u001b[39m# Python 3\u001b[39;00m\n\u001b[1;32m    443\u001b[0m     \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m--> 444\u001b[0m         httplib_response \u001b[39m=\u001b[39m conn\u001b[39m.\u001b[39;49mgetresponse()\n\u001b[1;32m    445\u001b[0m     \u001b[39mexcept\u001b[39;00m \u001b[39mBaseException\u001b[39;00m \u001b[39mas\u001b[39;00m e:\n\u001b[1;32m    446\u001b[0m         \u001b[39m# Remove the TypeError from the exception chain in\u001b[39;00m\n\u001b[1;32m    447\u001b[0m         \u001b[39m# Python 3 (including for exceptions like SystemExit).\u001b[39;00m\n\u001b[1;32m    448\u001b[0m         \u001b[39m# Otherwise it looks like a bug in the code.\u001b[39;00m\n\u001b[1;32m    449\u001b[0m         six\u001b[39m.\u001b[39mraise_from(e, \u001b[39mNone\u001b[39;00m)\n",
      "File \u001b[0;32m/usr/lib/python3.10/http/client.py:1374\u001b[0m, in \u001b[0;36mHTTPConnection.getresponse\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m   1372\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m   1373\u001b[0m     \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m-> 1374\u001b[0m         response\u001b[39m.\u001b[39;49mbegin()\n\u001b[1;32m   1375\u001b[0m     \u001b[39mexcept\u001b[39;00m \u001b[39mConnectionError\u001b[39;00m:\n\u001b[1;32m   1376\u001b[0m         \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mclose()\n",
      "File \u001b[0;32m/usr/lib/python3.10/http/client.py:318\u001b[0m, in \u001b[0;36mHTTPResponse.begin\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    316\u001b[0m \u001b[39m# read until we get a non-100 response\u001b[39;00m\n\u001b[1;32m    317\u001b[0m \u001b[39mwhile\u001b[39;00m \u001b[39mTrue\u001b[39;00m:\n\u001b[0;32m--> 318\u001b[0m     version, status, reason \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_read_status()\n\u001b[1;32m    319\u001b[0m     \u001b[39mif\u001b[39;00m status \u001b[39m!=\u001b[39m CONTINUE:\n\u001b[1;32m    320\u001b[0m         \u001b[39mbreak\u001b[39;00m\n",
      "File \u001b[0;32m/usr/lib/python3.10/http/client.py:279\u001b[0m, in \u001b[0;36mHTTPResponse._read_status\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    278\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m_read_status\u001b[39m(\u001b[39mself\u001b[39m):\n\u001b[0;32m--> 279\u001b[0m     line \u001b[39m=\u001b[39m \u001b[39mstr\u001b[39m(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mfp\u001b[39m.\u001b[39;49mreadline(_MAXLINE \u001b[39m+\u001b[39;49m \u001b[39m1\u001b[39;49m), \u001b[39m\"\u001b[39m\u001b[39miso-8859-1\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[1;32m    280\u001b[0m     \u001b[39mif\u001b[39;00m \u001b[39mlen\u001b[39m(line) \u001b[39m>\u001b[39m _MAXLINE:\n\u001b[1;32m    281\u001b[0m         \u001b[39mraise\u001b[39;00m LineTooLong(\u001b[39m\"\u001b[39m\u001b[39mstatus line\u001b[39m\u001b[39m\"\u001b[39m)\n",
      "File \u001b[0;32m/usr/lib/python3.10/socket.py:705\u001b[0m, in \u001b[0;36mSocketIO.readinto\u001b[0;34m(self, b)\u001b[0m\n\u001b[1;32m    703\u001b[0m \u001b[39mwhile\u001b[39;00m \u001b[39mTrue\u001b[39;00m:\n\u001b[1;32m    704\u001b[0m     \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m--> 705\u001b[0m         \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_sock\u001b[39m.\u001b[39;49mrecv_into(b)\n\u001b[1;32m    706\u001b[0m     \u001b[39mexcept\u001b[39;00m timeout:\n\u001b[1;32m    707\u001b[0m         \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_timeout_occurred \u001b[39m=\u001b[39m \u001b[39mTrue\u001b[39;00m\n",
      "File \u001b[0;32m/usr/lib/python3.10/ssl.py:1274\u001b[0m, in \u001b[0;36mSSLSocket.recv_into\u001b[0;34m(self, buffer, nbytes, flags)\u001b[0m\n\u001b[1;32m   1270\u001b[0m     \u001b[39mif\u001b[39;00m flags \u001b[39m!=\u001b[39m \u001b[39m0\u001b[39m:\n\u001b[1;32m   1271\u001b[0m         \u001b[39mraise\u001b[39;00m \u001b[39mValueError\u001b[39;00m(\n\u001b[1;32m   1272\u001b[0m           \u001b[39m\"\u001b[39m\u001b[39mnon-zero flags not allowed in calls to recv_into() on \u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m\n\u001b[1;32m   1273\u001b[0m           \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m\u001b[39m__class__\u001b[39m)\n\u001b[0;32m-> 1274\u001b[0m     \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mread(nbytes, buffer)\n\u001b[1;32m   1275\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[1;32m   1276\u001b[0m     \u001b[39mreturn\u001b[39;00m \u001b[39msuper\u001b[39m()\u001b[39m.\u001b[39mrecv_into(buffer, nbytes, flags)\n",
      "File \u001b[0;32m/usr/lib/python3.10/ssl.py:1130\u001b[0m, in \u001b[0;36mSSLSocket.read\u001b[0;34m(self, len, buffer)\u001b[0m\n\u001b[1;32m   1128\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m   1129\u001b[0m     \u001b[39mif\u001b[39;00m buffer \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m-> 1130\u001b[0m         \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_sslobj\u001b[39m.\u001b[39;49mread(\u001b[39mlen\u001b[39;49m, buffer)\n\u001b[1;32m   1131\u001b[0m     \u001b[39melse\u001b[39;00m:\n\u001b[1;32m   1132\u001b[0m         \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_sslobj\u001b[39m.\u001b[39mread(\u001b[39mlen\u001b[39m)\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "# 对此embeddings做了托管, 可直接下载无需计算\n",
    "# document_embeddings = load_embeddings(\"https://cdn.openai.com/API/examples/data/olympics_sections_document_embeddings.csv\")\n",
    "\n",
    "# ===== 或使用下面一行代码来重新计算embeddings ========\n",
    "\n",
    "document_embeddings = compute_doc_embeddings(df)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'document_embeddings' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn [14], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[39m# 示例embedding:\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m example_entry \u001b[39m=\u001b[39m \u001b[39mlist\u001b[39m(document_embeddings\u001b[39m.\u001b[39mitems())[\u001b[39m0\u001b[39m]\n\u001b[1;32m      3\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39m{\u001b[39;00mexample_entry[\u001b[39m0\u001b[39m]\u001b[39m}\u001b[39;00m\u001b[39m : \u001b[39m\u001b[39m{\u001b[39;00mexample_entry[\u001b[39m1\u001b[39m][:\u001b[39m5\u001b[39m]\u001b[39m}\u001b[39;00m\u001b[39m... (\u001b[39m\u001b[39m{\u001b[39;00m\u001b[39mlen\u001b[39m(example_entry[\u001b[39m1\u001b[39m])\u001b[39m}\u001b[39;00m\u001b[39m entries)\u001b[39m\u001b[39m\"\u001b[39m)\n",
      "\u001b[0;31mNameError\u001b[0m: name 'document_embeddings' is not defined"
     ]
    }
   ],
   "source": [
    "# 示例embedding:\n",
    "example_entry = list(document_embeddings.items())[0]\n",
    "print(f\"{example_entry[0]} : {example_entry[1][:5]}... ({len(example_entry[1])} entries)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "因此，我们将文档库拆分为多个部分，并通过创建表示每个块的嵌入向量对它们进行编码。接下来，我们将使用这些嵌入来回答用户的问题。\n",
    "\n",
    "# 2） 找到与问题嵌入最相似的文档嵌入\n",
    "\n",
    "在问答时，为了回答用户的查询，我们计算问题的查询嵌入，并使用它来查找最相似的文档部分。由于这是一个小示例，因此我们在本地存储和搜索嵌入。如果你有一个更大的数据集，考虑使用矢量搜索引擎，如[Pinecone]（https://www.pinecone.io/）或[Weaviate]（https://github.com/semi-technologies/weaviate）来支持搜索。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def vector_similarity(x: list[float], y: list[float]) -> float:\n",
    "    \"\"\"\n",
    "    Returns the similarity between two vectors.\n",
    "    \n",
    "    Because OpenAI Embeddings are normalized to length 1, the cosine similarity is the same as the dot product.\n",
    "    \"\"\"\n",
    "    return np.dot(np.array(x), np.array(y))\n",
    "\n",
    "def order_document_sections_by_query_similarity(query: str, contexts: dict[(str, str), np.array]) -> list[(float, (str, str))]:\n",
    "    \"\"\"\n",
    "    Find the query embedding for the supplied query, and compare it against all of the pre-calculated document embeddings\n",
    "    to find the most relevant sections. \n",
    "    \n",
    "    Return the list of document sections, sorted by relevance in descending order.\n",
    "    \"\"\"\n",
    "    query_embedding = get_embedding(query)\n",
    "    \n",
    "    document_similarities = sorted([\n",
    "        (vector_similarity(query_embedding, doc_embedding), doc_index) for doc_index, doc_embedding in contexts.items()\n",
    "    ], reverse=True)\n",
    "    \n",
    "    return document_similarities"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "order_document_sections_by_query_similarity(\"Who won the men's high jump?\", document_embeddings)[:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "order_document_sections_by_query_similarity(\"Who won the women's high jump?\", document_embeddings)[:5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以看到，每个问题最相关的文档部分包括男子和女子跳高比赛的摘要 - 这正是我们所期望的。\n",
    "\n",
    "# 3） 在查询提示中添加最相关的文档部分\n",
    "\n",
    "计算出最相关的上下文片段后，只需将它们附加到提供的查询中即可构建提示。使用查询分隔符来帮助模型区分单独的文本片段很有帮助。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "MAX_SECTION_LEN = 500\n",
    "SEPARATOR = \"\\n* \"\n",
    "ENCODING = \"gpt2\"  # encoding for text-davinci-003\n",
    "\n",
    "encoding = tiktoken.get_encoding(ENCODING)\n",
    "separator_len = len(encoding.encode(SEPARATOR))\n",
    "\n",
    "f\"Context separator contains {separator_len} tokens\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def construct_prompt(question: str, context_embeddings: dict, df: pd.DataFrame) -> str:\n",
    "    \"\"\"\n",
    "    Fetch relevant \n",
    "    \"\"\"\n",
    "    most_relevant_document_sections = order_document_sections_by_query_similarity(question, context_embeddings)\n",
    "    \n",
    "    chosen_sections = []\n",
    "    chosen_sections_len = 0\n",
    "    chosen_sections_indexes = []\n",
    "     \n",
    "    for _, section_index in most_relevant_document_sections:\n",
    "        # Add contexts until we run out of space.        \n",
    "        document_section = df.loc[section_index]\n",
    "        \n",
    "        chosen_sections_len += document_section.tokens + separator_len\n",
    "        if chosen_sections_len > MAX_SECTION_LEN:\n",
    "            break\n",
    "            \n",
    "        chosen_sections.append(SEPARATOR + document_section.content.replace(\"\\n\", \" \"))\n",
    "        chosen_sections_indexes.append(str(section_index))\n",
    "            \n",
    "    # Useful diagnostic information\n",
    "    print(f\"Selected {len(chosen_sections)} document sections:\")\n",
    "    print(\"\\n\".join(chosen_sections_indexes))\n",
    "    \n",
    "    header = \"\"\"Answer the question as truthfully as possible using the provided context, and if the answer is not contained within the text below, say \"I don't know.\"\\n\\nContext:\\n\"\"\"\n",
    "    \n",
    "    return header + \"\".join(chosen_sections) + \"\\n\\n Q: \" + question + \"\\n A:\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = construct_prompt(\n",
    "    \"Who won the 2020 Summer Olympics men's high jump?\",\n",
    "    document_embeddings,\n",
    "    df\n",
    ")\n",
    "\n",
    "print(\"===\\n\", prompt)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们现在获得了与该问题最相关的文档部分。作为最后一步，让我们把它们放在一起以获得问题的答案。\n",
    "\n",
    "# 4） 根据上下文回答用户的问题。\n",
    "\n",
    "现在，我们已经检索了相关上下文并构造了提示，我们终于可以使用完成 API 来回答用户的查询。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "COMPLETIONS_API_PARAMS = {\n",
    "    # We use temperature of 0.0 because it gives the most predictable, factual answer.\n",
    "    \"temperature\": 0.0,\n",
    "    \"max_tokens\": 300,\n",
    "    \"model\": COMPLETIONS_MODEL,\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def answer_query_with_context(\n",
    "    query: str,\n",
    "    df: pd.DataFrame,\n",
    "    document_embeddings: dict[(str, str), np.array],\n",
    "    show_prompt: bool = False\n",
    ") -> str:\n",
    "    prompt = construct_prompt(\n",
    "        query,\n",
    "        document_embeddings,\n",
    "        df\n",
    "    )\n",
    "    \n",
    "    if show_prompt:\n",
    "        print(prompt)\n",
    "\n",
    "    response = openai.Completion.create(\n",
    "                prompt=prompt,\n",
    "                **COMPLETIONS_API_PARAMS\n",
    "            )\n",
    "\n",
    "    return response[\"choices\"][0][\"text\"].strip(\" \\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "answer_query_with_context(\"Who won the 2020 Summer Olympics men's high jump?\", df, document_embeddings)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过结合嵌入和完成 API，我们创建了一个问答模型，该模型可以使用大量附加知识来回答问题。当它不知道答案时，它也明白！\n",
    "\n",
    "在本例中，我们使用了维基百科文章的数据集，但该数据集可以替换为书籍、文章、文档、服务手册等。**我们迫不及待地想看看您使用 GPT-3 创建的内容！\n",
    "\n",
    "# 更多例子\n",
    "\n",
    "让我们玩得开心，并尝试更多的例子。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"Why was the 2020 Summer Olympics originally postponed?\"\n",
    "answer = answer_query_with_context(query, df, document_embeddings)\n",
    "\n",
    "print(f\"\\nQ: {query}\\nA: {answer}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"In the 2020 Summer Olympics, how many gold medals did the country which won the most medals win?\"\n",
    "answer = answer_query_with_context(query, df, document_embeddings)\n",
    "\n",
    "print(f\"\\nQ: {query}\\nA: {answer}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"What was unusual about the men’s shotput competition?\"\n",
    "answer = answer_query_with_context(query, df, document_embeddings)\n",
    "\n",
    "print(f\"\\nQ: {query}\\nA: {answer}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"In the 2020 Summer Olympics, how many silver medals did Italy win?\"\n",
    "answer = answer_query_with_context(query, df, document_embeddings)\n",
    "\n",
    "print(f\"\\nQ: {query}\\nA: {answer}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "目前问答模型不太容易出现幻觉，并且对它知道或不知道什么有更明确的认知。当信息不包含在上下文中时，当问题无厘头，或者当问题在理论上可以回答但超出了 GPT-3 的权限时都是有效的！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"What is the total number of medals won by France, multiplied by the number of Taekwondo medals given out to all countries?\"\n",
    "answer = answer_query_with_context(query, df, document_embeddings)\n",
    "\n",
    "print(f\"\\nQ: {query}\\nA: {answer}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"What is the tallest mountain in the world?\"\n",
    "answer = answer_query_with_context(query, df, document_embeddings)\n",
    "\n",
    "print(f\"\\nQ: {query}\\nA: {answer}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"Who won the grimblesplatch competition at the 2020 Summer Olympic games?\"\n",
    "answer = answer_query_with_context(query, df, document_embeddings)\n",
    "\n",
    "print(f\"\\nQ: {query}\\nA: {answer}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.6 ('py310')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.6"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "49ec5068d4d6d3736c39d3918a16f1ca551a23384109b69898c01c2015d1370c"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
